#include "commonform.h"
#include "ui_commonform.h"
#include <qmovie.h>
#include <qlabel.h>
#include "datamanagement/mainwindows/mainwindow.h"

//==========================================默认构造函数==============================
CommonForm::CommonForm(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::CommonForm)
{
    ui->setupUi(this);
    // 默认构造什么也不操作，因为没有tableName,界面数据初始化基于tableName
    // 调用默认构造，可通过调用loadView(UserdefineTableEntity table, QProgressBar* progressBar)来初始化页面数据
    // 或者通过setTable(UserdefineTableEntity table)，setProgressBar(QProgressBar *progressBar), 然后调用loadView()来初始化页面数据
}

//==========================================析构函数==============================
CommonForm::~CommonForm()
{
    delete ui;
    delete conditionListWidget;
    delete conditionLineEdit;
    delete columnHideShowListWidget;
    delete columnHideShowLineEdit;
}

//==========================================设置当前界面组件对象对应的表信息=====================================================
void CommonForm::setTable(UserDefineTableEntity table)
{
    this->table = table;
}

//==========================================获取当前界面组件对象对应的表信息=====================================================
UserDefineTableEntity CommonForm::getTable()
{
    return this->table;
}

//==========================================加载界面方式1（先调用setTable在调用本方法）=======
void CommonForm::loadView()
{
    // 清除表格所有数据，包括表头等
    ui->information_tableWidget->clear();

    // 表格简单设置
    ui->information_tableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);  //设置水平滚动条
    ui->information_tableWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);   //设置垂直滚动条

    // 设置分页的单页显示数量选项
    ui->signle_page_show_num_comboBox->addItems({"10","20","30","50","100","200","300","500"});

    // 创建列表头
    updateUserdefineQTableWidgetHeader();

    // 初始化表格数据
    initUserdefineTable();

    // 刷新条件搜索下拉框列表
    updateSelectColumnCombobox();

    // 刷新隐藏显示列下拉框列表
    updateColumnHideShowCombobox();
}

//==========================================加载界面方式2（直接调用本方法加载界面）==============================
void CommonForm::loadView(UserDefineTableEntity table)
{
    this->table = table;
    loadView(); // 调用加载界面方式1
}

//==========================================【修改信息按钮】点击事件==============================================
void CommonForm::on_change_pushButton_clicked()
{
    // 获取界面表格选中的行的id集合
    QList<int> ids = TableWidgetOpt::getQTableWidgetCheckedIds(ui->information_tableWidget);
    qDebug()<<"选中大小:"<<ids.size();
    if(ids.size() == 0) {
        QMessageBox::information(this,"提示","请勾选要修改的行数据");
        return;
    }
    // 保证修改时只能单次修改一行数据
    if(ids.size() > 1) {
        QMessageBox::information(this,"提示","只能勾选一行");
        return;
    }
    // 打开修改界面
    updateDialog* update = new updateDialog(this);
    update->setAttribute(Qt::WA_DeleteOnClose); // 关闭时销毁对象
    update->setModal(true); // 设置为模态窗口，即窗口打开后只能该修改界面操作，不能在其他窗口进行操作，保证操作时的唯一性
    update->initLayOut(ids.at(0));
    update->show();
}

//==========================================【添加信息按钮】点击事件==============================================
void CommonForm::on_insert_pushButton_clicked()
{
    // 打开添加信息窗口
    addDialog* addWidow =  new addDialog(this);
    addWidow->setAttribute(Qt::WA_DeleteOnClose); // 关闭时销毁对象
    addWidow->setModal(true); // 设置为模态窗口
    addWidow->show();
}

//==========================================【删除信息按钮】点击事件==============================================
void CommonForm::on_delete_pushButton_clicked()
{
    // 获取界面表格选中的行的id集合
    QList<int> ids = TableWidgetOpt::getQTableWidgetCheckedIds(ui->information_tableWidget);
    qDebug()<<"选中表格行大小:"<<ids.size();
    if(ids.size() == 0) {
        QMessageBox::information(this,"提示","请勾选要删除的行数据");
        return;
    }
    int sign = QMessageBox::information(this, "提示", "删除后不可恢复，确认删除", "确认","取消", 0, 1);
    if(sign == 1) { // 点击取消按钮直接返回
        return;
    }
    // 设置主界面进度条5秒后到达90%
    ((MainWindow*) UserDefineConstant::MAIN_WINDOW)->changeProgressBar(5, 90);
    // 开启一个加载提示框
    LoadingDialog* loading = LoadingDialog::createLoading(this);
    // 创建一个异步线程监听器
    QFutureWatcher<QPair<bool,MessageInfo>> *pwatcher = new QFutureWatcher<QPair<bool,MessageInfo>>;

    // 异步执行耗时操作（异步相当于重新开了个子线程去执行，所以异步里面执行的内容在子线程中，子线程中不能修改和创建GUI的对象），这里用到了lambda表达式,自行搜索c++lambda表达式
    // 对异步操作有疑问：https://blog.csdn.net/nchu_zhangyiqing/article/details/114291400
    // 对c++lambda有疑问：https://blog.csdn.net/qq_37085158/article/details/124626913
    QFuture<QPair<bool,MessageInfo>> future = QtConcurrent::run([=]() {
        // 根据id集合删除该界面对象对应的表中指定的数据
        bool isSuccess = DataBaseOpt::removeTableByIds(table.tableName,ids);
        if(isSuccess){
            return QPair<bool,MessageInfo>(true, MessageInfo("成功","删除成功"));
        }
        return QPair<bool,MessageInfo>(false, MessageInfo("失败","删除失败"));
    });

    // 绑定异步耗时操作执行完成后的后续操作（也就是监听结束的操作）（该后续操作是在主线程中的，可以操作GUI对象）
    connect(pwatcher, &QFutureWatcher<QPair<bool,MessageInfo>>::finished, this, [=]()
    {
        QPair<bool,MessageInfo> result = pwatcher->future().result();
        // 设置主界面进度条立即到达100%
        ((MainWindow*) UserDefineConstant::MAIN_WINDOW)->changeProgressBar(0, 100);
        // 关闭加载提示框
        loading->close();
        if(result.first) {
            // 刷新列表
            autoUserdefineTableReflash(true);
        }
        // 提示信息放在最后，避免阻塞前面内容的执行
        QMessageBox::information(this,result.second.title, result.second.msg);
    });
    pwatcher->setFuture(future); // 设置监听器监听内容为异步操作的返回内容（也就是绑定监听的内容，这里是绑定异步耗时操作）
}

//==========================================【查询信息按钮】点击事件==============================================
void CommonForm::on_refer_pushButton_clicked()
{
    // 获取查询条件组合下拉框选中行的数据
    QList<QString> checkedData = getComboboxCheckedSelectColumns(conditionListWidget);
    Qt::CheckState conditionCheck = ui->condition_checkBox->checkState();
    // 获取界面条件输入框值
    QString seachText = ui->seach_lineEdit->text().trimmed();
    if(conditionCheck != Qt::CheckState::Checked && checkedData.size() < 1 && !ObjectUtil::isEmpty(seachText)) { // 如果填入了查询信息，并且下拉框未选择，自定义条件复选框也没选择，则提示错误信息
        QMessageBox::information(this,"提示","请先选择条件下拉框组合查询,勾选组合后，在进行关键词匹配时会匹配多个相关组合的数据，查询采用模糊查询，输入关键词会查询含有关键词的相关数据;或者勾选自定义条件复选框，按照规则自定义查询条件");
        return;
    }

    // 开启一个加载提示框
    LoadingDialog* loading = LoadingDialog::createLoading(false, this);
    // 创建一个异步线程监听器
    QFutureWatcher<QPair<bool,MessageInfo>> *pwatcher = new QFutureWatcher<QPair<bool,MessageInfo>>;

    // 异步执行耗时操作（异步相当于重新开了个子线程去执行，所以异步里面执行的内容在子线程中，子线程中不能修改和创建GUI的对象），这里用到了lambda表达式,自行搜索c++lambda表达式
    // 对异步操作有疑问：https://blog.csdn.net/nchu_zhangyiqing/article/details/114291400
    // 对c++lambda有疑问：https://blog.csdn.net/qq_37085158/article/details/124626913
    QFuture<QPair<bool,MessageInfo>> future = QtConcurrent::run([=]() {
        // 刷新界面数据
        autoUserdefineTableReflash(true);
        return QPair<bool,MessageInfo>(true, MessageInfo("成功","查询成功"));
    });

    // 绑定异步耗时操作执行完成后的后续操作（也就是监听结束的操作）（该后续操作是在主线程中的，可以操作GUI对象）
    connect(pwatcher, &QFutureWatcher<QPair<bool,MessageInfo>>::finished, this, [=]()
    {
        // 关闭加载提示框
        loading->close();
    });
    pwatcher->setFuture(future); // 设置监听器监听内容为异步操作的返回内容（也就是绑定监听的内容，这里是绑定异步耗时操作）
}

//==========================================【提示信息按钮】点击事件==============================================
void CommonForm::on_hint_pushButton_clicked()
{
    // 打开信息提示窗口
    informationDialog *inforWidow = informationDialog::create("infoTitle", this);
    if (!ObjectUtil::isNull(inforWidow)) {
        // 设置窗体的大小
        inforWidow->setFixedSize(900, 700);
        // 查询出当前列表展示所有的字段列信息
        QList<QueryConditions> columnConditions;
        columnConditions.append(QueryConditions(UserDefineConstant::TABLE_CONNECT_TABLE, table.tableName, false, "=", "or"));
        QList<ColumnEntity> columnEntitys = DataBaseOpt::selectAllColumnTable(columnConditions); // 从自定义字段表查询出当前界面对象对应表的所有属性字段信息
        QString info = "";
        // 循环组装对应表的字段中文名称和字段英文名称之间对应的信息
        for(ColumnEntity columnEntity: columnEntitys) {
            info = info + columnEntity.columnZnName + "    " +columnEntity.columnName + "\n";
        }
        inforWidow->setInfo("当前表数据中英文字段名的对应关系：\n" +
                            info + "\n"
                            "查询方式有两种：\n"
                            "1、选择条件组合下拉框,勾选组合后，再在输入框中输入需要查询的内容，在进行关键词匹配时会匹配多个相关组合的数据，查询采用模糊查询，输入的内容会对勾选的字段名进行相关数据匹配\n"
                            "2、自定义查询条件的方式,勾选上‘自定义条件’复选框，则直接在输入框填写sql的where语句后的查询条件进行匹配查询\n"
                            "注：因为当前的所有字段存储都是字符串，所以在进行条件编写时，匹配字段名必须是字段英文名，匹配字段值必须用英文的单引号或者双引号包裹起来，只有id是int类型，也就是数值。你用英文引号包裹\n\n"
                            "匹配的连接关键词有：\n"
                            "and 表示‘且’的意思\n"
                            "or  表示‘或者’的意思\n\n"
                            "匹配的查询条件有：\n"
                            "<           表示‘小于’的意思\n"
                            "<=          表示‘小于等于’的意思\n"
                            "=           表示‘等于’的意思\n"
                            ">           表示‘大于’的意思\n"
                            ">=          表示‘大于等于’的意思\n"
                            "!=          表示‘不等于’的意思\n"
                            "like        表示‘模糊查询匹配’的意思\n"
                            "in          表示‘匹配在in中指定的关键词’的意思\n"
                            "between and 表示‘匹配在两个值之间的数据’的意思\n"
                            "说明:\n"
                            "like表示模糊匹配，可以进行左匹配，右匹配，全匹配; 比如查找姓李的，则用 like '李%',表示李开头; 比如查找末尾带雷字的，则用 like '%雷',表示结尾是雷的; 比如查找字符带小字的，则用 like '%小%'\n"
                            "in表示匹配在in中指定的关键词;比如要查找名字为张三、李四、王五的数据，则用 in ('张三','李四','王五')\n"
                            "between and表示匹配两个之间的数据; 比如我要找出2020-01-01到2020-01-31之间的数据，则可以用between '2020-01-01' and '2020-01-31'\n\n"
                            "完整例子：\n"
                            "1)比如我要查找名字等于小明或者小王\n"
                            "name = '小明' or name = '小王'\n"
                            "或者\n"
                            "name in ('小明','小王')\n"
                            "2)比如我要查找日期大于等于2018-01-01且小于等于2020-01-01的数据\n"
                            "date <= '2018-01-01' and date >= '2020-01-01'\n"
                            "或者\n"
                            "date between '2018-01-01' and '2020-01-01'\n"
                            "3)比如我要查找所有姓李的或者是名字末尾带有伟的或者是名字里面有益字的\n"
                            "name like '李%' or name like '%伟' or name like '%益%'\n");
        inforWidow->show();
    }
}

//==========================================【编辑字段按钮】点击事件==============================================
void CommonForm::on_edit_column_pushButton_clicked()
{
    // 打开编辑字段界面
    columnSettingDialog* columnSetting = new columnSettingDialog(this);
    columnSetting->setAttribute(Qt::WA_DeleteOnClose); // 退出时销毁对象
    columnSetting->setModal(true); // 设置为模态框
    columnSetting->show();
}

//==========================================【窗口大小改变】事件=================================================
void CommonForm::resizeEvent(QResizeEvent *event)
{
    // 窗口尺寸变化动态改变表格列宽
    autoAdjustmentTableWidth();
}

//==========================================【绘制】事件=======================================================
void CommonForm::paintEvent(QPaintEvent *event)
{
    if(isWindowHasStarted) {
        return;
    }
    // 当窗口初始化绘制时改变表格大小
    autoAdjustmentTableWidth();

    // 改变isWidowSatrtAdjustmentTable为true,则后续的绘制事件就会在上一步被拦截，不进行表格处理，我们要的是在初始化时对表格进行一个自适应适配
    //，但是如果在构造函数里面处理则不行，因为构造函数中获取的全是初始值，不是调整过后的组件大小和参数
    // 构造函数里面只是在准备对象，也就是准备属性设置的参数等操作，绘制时才去读取我们在构造时设置的一些参数，在进行界面的绘制
    // 真正的改变其实是在绘制之后，所以我们要在绘制里面来处里对表格初始设置才能有效果
    changeWindowHasStartedStatus(true);
}

//==========================================自动调整界面表格列宽方法=================================================
void CommonForm::autoAdjustmentTableWidth()
{
    // 默认的自动调整列宽的配置参数
    QMap<int,int> columnWidthMap;
    columnWidthMap.insert(0,100);
    TableWidgetOpt::autoAdjustmentQTableWidgetWidth(ui->information_tableWidget,800,120,columnWidthMap);
}

//==========================================设置起动时是否已经调整过表格（isWindowHasStarted的值）=======================
void CommonForm::changeWindowHasStartedStatus(bool status)
{
    this->isWindowHasStarted = status;
}

//==========================================刷新界面表格数据（根据指定页码、单页行数、查询条件）=======================
int CommonForm::userdefineTableReflash(int page, int size, QList<QueryConditions> conditions)
{
    QList<QueryConditions> columnConditions;
    columnConditions.append(QueryConditions(UserDefineConstant::TABLE_CONNECT_TABLE, table.tableName, false, "=", "or"));
    QList<ColumnEntity> columnEntitys = DataBaseOpt::selectAllColumnTable(columnConditions); // 从自定义字段表查询出当前界面对象对应表的所有属性字段信息
    QList<QString> columnNames;
    // 循环组装对应表的字段名称集合
    for(ColumnEntity columnEntity: columnEntitys) {
        columnNames.append(columnEntity.columnName);
    }
    // 刷新表格数据
    return TableWidgetOpt::selectDataBaseTableDataToQTableWidget(ui->information_tableWidget, table.tableName, columnNames, page, size, conditions, true);
}

//==========================================自动刷新界面表格数据（是否从起始页刷新）=======================
void CommonForm::autoUserdefineTableReflash(bool isStart)
{
    qDebug()<<"进入"+table.tableZnName+"数据刷新";
    // 获取界面当前页码
    QString page = ui->now_page_label->text().trimmed();
    if(isStart) { // 如果从起始页开始，则设置当前页码为第一页
        page = "1";
    }
    // 获取界面单页显示数据行数
    int size = ui->signle_page_show_num_comboBox->currentText().toInt();
    QList<QueryConditions> conditions; // 查询条件
    // 获取界面条件输入框值
    QString seachText = ui->seach_lineEdit->text().trimmed();
    qDebug()<<"查询输入框:"<< seachText <<"是否空："<<ObjectUtil::isEmpty(seachText);
    if(!ObjectUtil::isEmpty(seachText)) { // 如果输入值不为空，则构造查询条件
        QList<QueryConditions> columnConditions;
        columnConditions.append(QueryConditions(UserDefineConstant::TABLE_CONNECT_TABLE, table.tableName, false, "=", "or"));
        QList<ColumnEntity> colums = DataBaseOpt::selectAllColumnTable(columnConditions); // 查询出该界面表格对应表的属性字段（列）信息集合
        if (ui->condition_checkBox->checkState() == Qt::CheckState::Checked) { // 如果勾选了自定义条件复选框，则按照自定义规则执行
            // 直接设置为where后面的查询语句
            conditions.append(QueryConditions(seachText));
        } else { // 未勾选自定义条件复选框，则按照选择下拉框的字段按照模糊查询规则
            QList<QString> checkedData = getComboboxCheckedSelectColumns(conditionListWidget); // 获取查询条件组合下拉框选中的值集合
            for(QString columnZnName : checkedData) { // 循环下拉框选中行数据
                for(ColumnEntity columnEntity:colums) { // 循环界面表格对应表的所有属性字段信息
                    if(columnEntity.columnZnName == columnZnName) { // 下拉框选中的行和当前循环的属性字段对象的字段中文名相等则构造条件
                        conditions.append(QueryConditions(columnEntity.columnName, seachText, false, "like%", "or")); // 根据下拉框选中的属性构造模糊查询条件
                        break;
                    }
                }
            }
        }
    }
    int nowPage = page.toInt(); // 当前页码，字符串转int
    int allRow = userdefineTableReflash(nowPage, size, conditions); // 刷新界面表格数据，返回得到数据库中当前表格对应表的所有数据总数
    ui->all_count_label->setText(QString::number(allRow)); // 设置数据总数
    if(allRow > 0) {
        int pageCount = allRow / size; // 总数据量除以单页显示行数，等于总页码
        if(allRow - (pageCount*size) > 0) { // 总数据量比总页码所有数据之和大，则总页码加1（这是因为上面的除法会产生余数，多出的部分总页码就要加1）
            pageCount = pageCount + 1;
        }
        ui->all_page_count_label->setText(QString::number(pageCount)); // 设置总页码
    }
    ui->now_page_label->setText(QString::number(nowPage)); // 设置当前页码
}

//==========================================初始化界面表格数据=======================
void CommonForm::initUserdefineTable()
{

    int page = 1; // 默认当前页码为1
    int size = 10; // 默认单页数据行数为10
    int allRow = userdefineTableReflash(page,size,QList<QueryConditions>()); // 刷新界面表格数据，返回得到数据库中当前表格对应表的所有数据总数
    ui->all_count_label->setText(QString::number(allRow)); // 设置总数据量
    if(allRow > 0) {
        int pageCount = allRow / size;
        if(allRow - (pageCount*size) > 0) {
            pageCount = pageCount + 1;
        }
        ui->all_page_count_label->setText(QString::number(pageCount)); // 设置总页码
    }
    ui->now_page_label->setText(QString::number(page));  // 设置当前页码
}

//==========================================修改更新界面表格的表头=======================
void CommonForm::updateUserdefineQTableWidgetHeader()
{
    // 构造关联表查询条件（界面表格对应表）
    QList<QueryConditions> columnConditions;
    columnConditions.append(QueryConditions(UserDefineConstant::TABLE_CONNECT_TABLE, table.tableName, false, "=", "or"));
    QList<ColumnEntity> columnEntitys = DataBaseOpt::selectAllColumnTable(columnConditions); // 从自定义字段表查询出界面表格对应表的所有属性字段信息
    QList<QString> columnZnNames; // 属性字段中文名集合
    columnZnNames.append(UserDefineConstant::CHECK_BOX_CN_NAME); // 复选框列（固定包括）
    columnZnNames.append(UserDefineConstant::ID_ZN_NAME); // 主键id列（固定包括）
    QString header = UserDefineConstant::CHECK_BOX_CN_NAME+"," + UserDefineConstant::ID_ZN_NAME;
    for(ColumnEntity columnEntity: columnEntitys) {
        columnZnNames.append(columnEntity.columnZnName); // 将查询出的属性字段中文名添加到集合中
        header = header + "," + columnEntity.columnZnName;
    }
    qDebug()<<"列表头查询为:"<< header;
    // 根据查询出来的属性字段中文名创建表头
    TableWidgetOpt::createQTableWidgetHeader(ui->information_tableWidget, columnZnNames);
}

//==========================================获取当前界面对象对应表的所有属性字段信息=======================
QList<ColumnEntity> CommonForm::getCurrentObjectAllColumn()
{
    if(ObjectUtil::isEmpty(table.tableName)) {
        return QList<ColumnEntity>();
    }
    // 构造关联表查询条件（当前界面对象对应表）
    QList<QueryConditions> columnConditions;
    columnConditions.append(QueryConditions(UserDefineConstant::TABLE_CONNECT_TABLE, table.tableName, false, "=", "or"));
    QList<ColumnEntity> columnEntitys = DataBaseOpt::selectAllColumnTable(columnConditions); // 从自定义字段表查询出当前界面对象对应表的所有属性字段信息
    return columnEntitys;
}

//==========================================修改更新界面查询条件下拉框数据=======================
void CommonForm::updateSelectColumnCombobox()
{
    // 取消条件下拉列表每项的信号
    int count = conditionListWidget->count();
    for(int i=0;i<count;i++) {
        QListWidgetItem *pItem = conditionListWidget->item(i);
        QCheckBox *pCheckBox = (QCheckBox *) conditionListWidget->itemWidget(pItem);
        pCheckBox->disconnect(SIGNAL(stateChanged(int)));
    }
    // 取消条件下拉选择框显示提示的信号
    conditionLineEdit->disconnect(SIGNAL(textChanged(QString)));
    // 清除条件下拉内容
    ui->select_column_comboBox->clear();
    conditionListWidget->clear();
    conditionLineEdit->clear();

    // 根据关联表构造查询条件
    QList<QueryConditions> columnConditions;
    columnConditions.append(QueryConditions(UserDefineConstant::TABLE_CONNECT_TABLE, table.tableName, false, "=", "or"));
    // 从自定义字段表中查询出指定关联表所有的字段信息
    QList<ColumnEntity> columnEntitys = DataBaseOpt::selectAllColumnTable(columnConditions);
    qDebug()<<"添加条件多选框:"<< columnEntitys.size();
    int row = 0;
    // 循环查询出的所有字段信息，将这些信息添加到搜索条件组合下拉框中
    for (ColumnEntity columnEntity: columnEntitys)
    {
        QListWidgetItem *pItem = new QListWidgetItem(conditionListWidget);
        conditionListWidget->addItem(pItem);
        pItem->setData(Qt::UserRole, row);
        QCheckBox *pCheckBox = new QCheckBox(this); // 复选框
        pCheckBox->setText(columnEntity.columnZnName); // 复选框的值
        pCheckBox->setToolTip(columnEntity.columnZnName); // 鼠标放在复选框上悬浮提示的值
        conditionListWidget->addItem(pItem);
        conditionListWidget->setItemWidget(pItem, pCheckBox);
        // 复选框状态变化时绑定修改显示内容的操作（这里绑定是和复选框的选择的改变事件进行绑定）
        connect(pCheckBox, SIGNAL(stateChanged(int)), this, SLOT(conditionListWidgetStatusChanged(int)));
        row++;
    }

    // 这个判断是避免重复设置，重复设置会报错
    if(!isInitConditionComboBoxSetting) {
        ui->select_column_comboBox->setModel(conditionListWidget->model());
        ui->select_column_comboBox->setView(conditionListWidget);
        ui->select_column_comboBox->setLineEdit(conditionLineEdit); // 设置下拉框显示的输入框
    }

    // 设置显示选中的信息或默认信息
    getAndSetTitleShowForComboboxCheckedSelectColumns(conditionListWidget, conditionLineEdit, "请勾选条件组合");
    conditionLineEdit->setReadOnly(true); // 显示的输入框只读
    ui->select_column_comboBox->setFixedWidth(150); // 设置下拉框固定宽度
    // 设置这个名称是因为针对这个在qss中有针对性的样式设置(也就是system.css文件中)
    conditionLineEdit->setObjectName("conditionLineEdit");
    // 显示改变事件时，按规则显示内容（这里的绑定是因为，在点击下拉框，点击下拉框每行的没被复选框右边覆盖的部分，显示内容会被conditionListWidget的列表项内容覆盖，
    // 因为下拉框每行是由列表项和列表项上层的复选框构成，复选框不能完全覆盖列表项，没覆盖的部分，我们点击就会把列表项的text用来显示，
    // 而conditionListWidget的item的显示text实际是空的，所以需要在显示改变时,去恢复正确的显示）
    connect(conditionLineEdit,SIGNAL(textChanged(QString)),this,SLOT(conditionListWidgetTextChanged(QString)));

    // 表名已经初始化了条件下拉框的设置
    isInitConditionComboBoxSetting = true;
}

//==========================================修改更新界面显示隐藏列下拉框数据=======================
void CommonForm::updateColumnHideShowCombobox()
{
    // 取消列显示隐藏下拉列表每项的信号
    int count = columnHideShowListWidget->count();
    for(int i=0;i<count;i++) {
        QListWidgetItem *pItem = columnHideShowListWidget->item(i);
        QCheckBox *pCheckBox = (QCheckBox *) columnHideShowListWidget->itemWidget(pItem);
        pCheckBox->disconnect(SIGNAL(stateChanged(int)));
    }
    // 取消列显示隐藏下拉选择框显示提示的信号
    columnHideShowLineEdit->disconnect(SIGNAL(textChanged(QString)));
    // 清除列显示隐藏下拉框内容
    ui->column_hide_show_comboBox->clear();
    columnHideShowListWidget->clear();
    columnHideShowLineEdit->clear();

    // 根据关联表构造查询条件
    QList<QueryConditions> columnConditions;
    columnConditions.append(QueryConditions(UserDefineConstant::TABLE_CONNECT_TABLE, table.tableName, false, "=", "or"));
    // 查询出自定义字段表中指定的关联表的所有字段信息
    QList<ColumnEntity> columnEntitys = DataBaseOpt::selectAllColumnTable(columnConditions);
    qDebug()<<"添加列显示隐藏多选框:"<< columnEntitys.size();
    int row = 0;
    // 循环查询到的字段信息，将这些信息添加到字段隐藏显示下拉框中
    for (ColumnEntity columnEntity: columnEntitys)
    {
        QListWidgetItem *pItem = new QListWidgetItem(columnHideShowListWidget);
        columnHideShowListWidget->addItem(pItem);
        pItem->setData(Qt::UserRole, row);
        QCheckBox *pCheckBox = new QCheckBox(this);
        pCheckBox->setText(columnEntity.columnZnName); // 复选框的值
        pCheckBox->setToolTip(columnEntity.columnZnName); // 鼠标放在复选框上悬浮提示的值
        // 默认添加新的列全部勾选
        pCheckBox->setChecked(true);
        columnHideShowListWidget->addItem(pItem);
        columnHideShowListWidget->setItemWidget(pItem, pCheckBox);
        // 复选框状态变化时绑定修改显示内容的操作（这里绑定是和复选框的选择的改变事件进行绑定）
        connect(pCheckBox, SIGNAL(stateChanged(int)), this, SLOT(columnhHideShowListWidgetStatusChanged(int)));
        row++;
    }

    // 这个判断是避免重复设置，重复设置会报错
    if(!isInitColumnHideShowComboBoxSetting) {
        ui->column_hide_show_comboBox->setModel(columnHideShowListWidget->model());
        ui->column_hide_show_comboBox->setView(columnHideShowListWidget);
        ui->column_hide_show_comboBox->setLineEdit(columnHideShowLineEdit); // 设置下拉框显示的输入框
    }


    // 设置显示选中的信息
    getAndSetTitleShowForComboboxCheckedSelectColumns(columnHideShowListWidget, columnHideShowLineEdit, "请选择展示列");
    columnHideShowLineEdit->setReadOnly(true); // 显示的输入框设置只读
    ui->column_hide_show_comboBox->setFixedWidth(120);
    // 设置这个名称是因为针对这个在qss中有针对性的样式设置，这里我们
    columnHideShowLineEdit->setObjectName("columnHideShowLineEdit");
    // 显示内容改变事件时，按规则显示内容（这里的绑定是因为，在点击下拉框，点击下拉框每行的没被复选框右边覆盖的部分，显示内容会被columnHideShowListWidget的列表项内容覆盖，
    // 因为下拉框每行是由列表项和列表项上层的复选框构成，复选框不能完全覆盖列表项，没覆盖的部分，我们点击就会把列表项的text用来显示，
    // 而columnHideShowListWidget的item的显示text实际是空的，所以需要在显示改变时,去恢复正确的显示）
    connect(columnHideShowLineEdit,SIGNAL(textChanged(QString)),this,SLOT(columnhHideShowListWidgetTextChanged(QString)));

    // 表名已经初始化了列显示隐藏下拉框的设置
    isInitColumnHideShowComboBoxSetting = true;
}

//==========================================查询条件组合下拉框显示【内容改变】事件=======================
void CommonForm::conditionListWidgetTextChanged(QString text)
{
    // 复用下面的显示选中内容的方法
    qDebug()<<"条件下拉列显示名事件触发:";
    conditionListWidgetStatusChanged(0);
}

//==========================================查询条件组合下拉框【复选框状态改变】事件=======================
void CommonForm::conditionListWidgetStatusChanged(int index)
{
    qDebug()<<"条件下拉框选中事件事件触发:";
    // 获取已经选中的条件下拉框勾选的列名集合，进行逗号拼接，作为下拉框的显示
    getAndSetTitleShowForComboboxCheckedSelectColumns(conditionListWidget, conditionLineEdit, "请选择条件组合");
}

//==========================================显示隐藏列下拉框显示【内容改变】事件=======================
void CommonForm::columnhHideShowListWidgetTextChanged(QString text)
{
    // 复用下面的显示选中内容的方法
    qDebug()<<"显示隐藏列下拉列显示名事件触发:";
    columnhHideShowListWidgetStatusChanged(0);
}

//==========================================显示隐藏列下拉框【复选框状态改变】事件=======================
void CommonForm::columnhHideShowListWidgetStatusChanged(int index)
{
    qDebug()<<"显示隐藏列下拉框选中事件事件触发:";
    // 获取已经选中的隐藏显示列下拉框勾选的列名集合，进行逗号拼接，作为下拉框的显示
    QList<QString> checkedNames = getAndSetTitleShowForComboboxCheckedSelectColumns(columnHideShowListWidget, columnHideShowLineEdit, "请选择展示列");
    // 循环每列，对每列复选框勾上的显示，没勾的隐藏
    int itemCount = ui->information_tableWidget->horizontalHeader()->count();
    for(int column=0;column<itemCount;column++) {
        QTableWidgetItem * item = ui->information_tableWidget->horizontalHeaderItem(column);
        // 跳过选择列和id列，不进行显示隐藏设置，因为这两列不存储在数据库，也不存在于下拉框
        QString text = item->text(); // 表格列头名称
        if(text == UserDefineConstant::CHECK_BOX_CN_NAME || text == UserDefineConstant::ID_ZN_NAME) {
            continue;
        }
        // 包含选中的列设置显示，不包含的列设置隐藏
        if(checkedNames.contains(text)) {
            ui->information_tableWidget->setColumnHidden(column, false);
        } else {
            ui->information_tableWidget->setColumnHidden(column, true);
        }
    }
    // 隐藏后自动调整一下界面列表列宽
    autoAdjustmentTableWidth();
}

//==========================================获取下拉框列表选中的行数据（选中值集合）=================================
QList<QString> CommonForm::getComboboxCheckedSelectColumns(QListWidget* widget)
{
    int count = widget->count(); // 列表总行数
    QList<QString> datas;
    for(int i=0;i<count; i++) {
        QListWidgetItem* item = widget->item(i);
        QCheckBox* checkBox = (QCheckBox *) widget->itemWidget(item); // 复选框
        qDebug()<<"循环下拉框列:"<< checkBox->text()<<"状态："<<checkBox->checkState();
        if(checkBox->checkState() == Qt::CheckState::Checked){ // 状态为选中的记录下该复选框的数据
            datas.append(checkBox->text());
        }
    }
    return datas;
}

//==========================================获得指定下拉框选中的行数据集合并设置title显示，返回选中下拉框值集合=================================
QList<QString> CommonForm::getAndSetTitleShowForComboboxCheckedSelectColumns(QListWidget *widget, QLineEdit *conditionLineEdit, QString defaultTitleInfo)
{
    // 获取下拉框列表选中行的值集合
    QList<QString> checkedNames = getComboboxCheckedSelectColumns(widget);
    QString showInfo = "";
    int count = 0;
    // 循环集合，用逗号拼接选中行的值
    for(QString checkedName: checkedNames) {
        if(count == checkedNames.size() -1) {
            showInfo = showInfo + checkedName;
        } else {
            showInfo = showInfo + checkedName + ",";
        }
        count++;
    }
    // 如果选中下拉框列表行数量为空，则设置默认下拉框显示信息
    if(checkedNames.size() == 0) {
        conditionLineEdit->setText(defaultTitleInfo);
        conditionLineEdit->setToolTip(defaultTitleInfo);
    } else { // 选中下拉框拉列表行数据不为空，则设置拼接后的字符作为显示信息
        conditionLineEdit->setText(showInfo);
        conditionLineEdit->setToolTip(showInfo);
    }
    return checkedNames; //  返回下拉框选中行的值集合
}

//==========================================获得界面ui对象=================================
Ui::CommonForm* CommonForm::getUi()
{
    return ui;
}

//==========================================界面表格【单元格双击】事件=================================
void CommonForm::on_information_tableWidget_cellDoubleClicked(int row, int column)
{
    qDebug()<<"进入"<<table.tableZnName<<"表数据对应information_tableWidget表格双击单元格操作";
    // 修改该标志的值，告诉后续的cellChanged事件，可以进行修改操作
    informationTableCellUpdateBeforeInfo.isDoubleClick = true;
    informationTableCellUpdateBeforeInfo.row = row;
    informationTableCellUpdateBeforeInfo.column = column;
    informationTableCellUpdateBeforeInfo.text = ui->information_tableWidget->item(row, column)->text();
    qDebug()<<"修改"<<table.tableZnName<<"表数据对应information_tableWidget双击单元格操作,赋值："
           << "isDoubleClick = " << informationTableCellUpdateBeforeInfo.isDoubleClick
           << ",row = " << informationTableCellUpdateBeforeInfo.row
           << ",column = " << informationTableCellUpdateBeforeInfo.column
           << ",text = " << informationTableCellUpdateBeforeInfo.text;
}

//==========================================界面表格【单元格改变】事件=================================
void CommonForm::on_information_tableWidget_cellChanged(int row, int column)
{
    qDebug()<<"修改"<<table.tableZnName<<"表数据操作,读informationTableCellUpdateBeforeInfo值："
           << "isDoubleClick = " << informationTableCellUpdateBeforeInfo.isDoubleClick
           << ",row = " << informationTableCellUpdateBeforeInfo.row
           << ",column = " << informationTableCellUpdateBeforeInfo.column
           << ",text = " << informationTableCellUpdateBeforeInfo.text;
    // 判断是否是界面进行了编辑操作，因为这个槽函数在表格不管是内容改变，行数量改变，代码操作单元格等都会触发
    // 所以不管是表格初始化，还是任何单元格有变化就会触发这个槽函数，而我们在单元格变化时不一定要进行修改操作
    // 这里需要阻断，我们只需要界面通过双击单元格后进行的操作才认为是修改操作
    // 所以判断是否进行双击单元格，判断双击的单元格行列是否等于现在改变单元格的行列，判断双击时老的单元格内容是否不等于改变时单元格的内容
    // 任何一个不满足，都直接进行返回，这么判断原因是首先要确认时双击时进行的判断，行列值保证双击时和修改时是同一个单元格，内容判断是
    // 保证用户可能双击单元格后并没有修改内容，这样该事件就不会触发，同时用户再可能将进行一些刷新列表什么的操作，这样该事件就会被反复触发
    // 而因为之前点击了单元格，所以是否双击单元格的bool值是否为true，和行列相等的条件在刷新到相同单元格内容时一定是满足条件的，通过内容是否不同，
    // 不同才进行修改，就可以阻断这种操作到一半不操作的情况，从而保证修改流程不出错
    if(!(informationTableCellUpdateBeforeInfo.isDoubleClick
         && row == informationTableCellUpdateBeforeInfo.row
         && column == informationTableCellUpdateBeforeInfo.column
         && informationTableCellUpdateBeforeInfo.text != ui->information_tableWidget->item(row, column)->text())) {
        return;
    }
    // 进入阻塞模式，上面说到，很多情况会触发这个函数，如果不阻塞，处理方法中含有循环，判断等情况，因为数据相互干扰，可能发生程序死锁，导致崩溃
    // 这里阻塞后，那么表格所有的信号都会被阻塞，在本次处理结束前，就不会有其他情况进行干扰，避免死锁
    ui->information_tableWidget->blockSignals(true);

    // 修改处理
    infomationTableWidgetCellChangeUpdateTable(row, column);

    // 处理完成后解除阻塞模式
    ui->information_tableWidget->blockSignals(false);

    // 修改操作完之后恢复原始的是否进行修改操作的标志信息，保证下一次修改流程的从头开始
    informationTableCellUpdateBeforeInfo.isDoubleClick = false;
    informationTableCellUpdateBeforeInfo.row = 0;
    informationTableCellUpdateBeforeInfo.column = 0;
    informationTableCellUpdateBeforeInfo.text = QString();
}

//==========================================该对象界面对应表格information_tableWidget单元格改变修改数据库表操作=================================
void CommonForm::infomationTableWidgetCellChangeUpdateTable(int row, int column) {
    // 设置主界面进度条5秒后到达90%
    ((MainWindow*) UserDefineConstant::MAIN_WINDOW)->changeProgressBar(5, 90);
    // 开启一个加载提示框
    LoadingDialog* loading = LoadingDialog::createLoading(this);

    qDebug()<<"进入修改"<<table.tableZnName<<"表数据对应information_tableWidget表格操作";
    // 获取表格中修改的值
    QTableWidgetItem* item = ui->information_tableWidget->item(row, column);
    QString text = item->text().trimmed();
    // 获取修改单元格对应的列头(中文名列头)
    QTableWidgetItem* horizontalHeaderItem = ui->information_tableWidget->horizontalHeaderItem(column);
    QString columnZnName = horizontalHeaderItem->text(); // 列头中文名

    int idColumnNum = TableWidgetOpt::findQTableWidgetIdZnNameColumn(ui->information_tableWidget); // 获得修改行的id列所在索引值
    int updateId = ui->information_tableWidget->item(row, idColumnNum)->text().toInt(); // 获得id列在该行的值

    QList<ColumnEntity> columnEntitys = getCurrentObjectAllColumn(); // 获取当前对象表格对应表的所有列属性
    // 组装列名集合
    QList<QString> columnNames;
    for(ColumnEntity entity: columnEntitys) {
        columnNames.append(entity.columnName);
    }

    // 查找出当前表数据库中对应的数据
    QList<QueryConditions> conditions;
    conditions.append(QueryConditions(UserDefineConstant::ID_COLUMN_NUIQUE_NAME, QString::number(updateId), true, "=", "and"));
    QList<QMap<QString, QString>> oldData = DataBaseOpt::selectTable(table.tableName, columnNames, conditions); // 查找出当前修改行对应的老数据,这个集合只会有一条数据

    QMap<QString,QString> columnValues;
    for(ColumnEntity entity: columnEntitys) {
        // 找到当前修改的列的信息，进行校验验证，验证通过后记录下当前修改列对应值
        if(entity.columnZnName == columnZnName) {
            // 当前列如果是不允许为空，则如果是空值则提示错误信息
            if(entity.columnIsRequired == UserDefineConstant::BOOL_ZN_NAME_TRUE && ObjectUtil::isEmpty(text)) {
                // 设置主界面进度条立即到达100%
                ((MainWindow*) UserDefineConstant::MAIN_WINDOW)->changeProgressBar(0, 100);
                loading->close(); // 关闭加载提示框
                // 复原成原来的值
                item->setText(oldData.at(0).value(entity.columnName));
                QMessageBox::information(this,"提示",entity.columnZnName+"请输入完整");
                return;
            }
            // 当前列如果是具有唯一性，则不满足唯一性则提示错误信息
            if(entity.columnIsUnique == UserDefineConstant::BOOL_ZN_NAME_TRUE) {
                QList<QueryConditions> conditions;
                conditions.append(QueryConditions(entity.columnName, text, false, "=" , "and"));
                conditions.append(QueryConditions(UserDefineConstant::ID_COLUMN_NUIQUE_NAME, QString::number(updateId), true, "!=" , "and"));
                int count = DataBaseOpt::selectAllCount(table.tableName, conditions); // 这里查找是否除了本身还有其他相同内容的列
                if(count > 0) {
                    // 设置主界面进度条立即到达100%
                    ((MainWindow*) UserDefineConstant::MAIN_WINDOW)->changeProgressBar(0, 100);
                    loading->close(); // 关闭加载提示框
                    // 复原成原来的值
                    item->setText(oldData.at(0).value(entity.columnName));
                    QMessageBox::information(this,"提示",entity.columnZnName+"已存在，请重新输入");
                    return;
                }
            }
            // 通过验证后记录下该列对应值
            columnValues.insert(entity.columnName, text);
        }
    }

    // 修改数据库表对应的列值
    bool isSucess = DataBaseOpt::updateTableById(table.tableName, updateId, columnValues);
    // 设置主界面进度条立即到达100%
    ((MainWindow*) UserDefineConstant::MAIN_WINDOW)->changeProgressBar(0, 100);
    loading->close(); // 关闭加载提示框
    if(isSucess) {
        // 刷新列表
        autoUserdefineTableReflash(false);
        QMessageBox::information(this,"成功","修改成功");
    }
    else {
        // 失败复原成原来的值
        item->setText(oldData.at(0).value(columnValues.keys().at(0)));
        QMessageBox::information(this,"失败","修改失败");
    }

}

//==========================================界面单页显示数据量下拉框【内容改变】事件=================================
void CommonForm::on_signle_page_show_num_comboBox_currentTextChanged(const QString &arg1)
{
    qDebug()<<"当前选择单页数量:"<< arg1;
    autoUserdefineTableReflash(true); // 自动从第一页刷新界面表格数据
}

//==========================================上一页按钮点击事件=================================
void CommonForm::on_pre_page_pushButton_clicked()
{
    qDebug()<<"上一页";
    int page = ui->now_page_label->text().toInt(); // 当前页码
    qDebug()<<"page:"<<page;
    if(page == 1) { // 如果当前页码已经在第一页，则直接返回，不进行操作
        return;
    }
    page = page - 1; // 页码减一，为上一页
    ui->now_page_label->setText(QString::number(page)); // 设置界面当前页码
    autoUserdefineTableReflash(false); // 自动刷新界面表格数据，不从第一页刷新，而是读取页面当前页码进行数据刷新
}

//==========================================下一页按钮点击事件=================================
void CommonForm::on_next_page_pushButton_clicked()
{
    qDebug()<<"下一页";
    int page = ui->now_page_label->text().toInt(); // 当前页码
    int allPage = ui->all_page_count_label->text().toInt(); // 总页码
    qDebug()<<"page:"<<page<<",allPage"<<allPage;
    if(page == allPage) { // 如果当前页码已经到达总页码，则不进行处理，直接返回
        return;
    }
    page = page + 1; // 页码加一，为下一页
    ui->now_page_label->setText(QString::number(page)); // 设置界面当前页码
    autoUserdefineTableReflash(false); // 自动刷新界面表格数据，不从第一页刷新，而是读取页面当前页码进行数据刷新
}

//==========================================页码输入框【内容改变】事件=================================
void CommonForm::on_page_num_lineEdit_textChanged(const QString &nowPageText)
{
    QString allPage = ui->all_page_count_label->text(); // 总页码
    if(ObjectUtil::isEmpty(nowPageText)) { // 输入框输入为空，则直接返回
        return;
    }
    // 正则匹配大于1的整数，如果输入内容不是大于1的整数直接错误提示返回
    QRegExp rx("^\\d{1,}$");
    if(rx.indexIn(nowPageText) == -1) {
        QMessageBox::information(this,"提示","输入错误，请输入1-"+allPage+"的页码");
        return;
    }
    int nowPage =  nowPageText.toInt(); // 当前页码，string转int
    int allPageNumber = allPage.toInt(); // 总页码，string转int
    // 如果输入页码小于1，或者输入页码大于总页码，直接错误提示返回
    if(nowPage < 1 || nowPage > allPageNumber) {
        QMessageBox::information(this,"提示","输入错误，请输入1-"+allPage+"的页码");
        return;
    }
    ui->now_page_label->setText(QString::number(nowPage)); // 设置界面当前页码
    autoUserdefineTableReflash(false); // 自动刷新界面表格数据，不从第一页刷新，而是读取页面当前页码进行数据刷新
}

